package com.hmdp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.SeckillVoucherMapper;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.concurrent.*;

/**
 秒杀优化：
    利用redis中完成订单的抢购。利用阻塞队列实现异步订单入库
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl4 extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    private static final String SEC_KILL = "SEC_KILL";
    @Autowired
    SeckillVoucherMapper seckillVoucherMapper;

    @Autowired
    VoucherOrderMapper voucherOrderMapper;



    @Autowired
    ISeckillVoucherService seckillVoucherService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    RedissonClient redissonClient;

    private static DefaultRedisScript<Long> SEC_KILL_SCRIPY;

    BlockingQueue<VoucherOrder> ORDER_QUEUE = new ArrayBlockingQueue(1024 * 1024);

    VoucherOrderServiceImpl4 proxy;

    //用于异步入库订单
    ExecutorService order_task_executor = Executors.newSingleThreadExecutor();





    @Autowired
    RedisIdWorker redisIdWorker;

    static {
        SEC_KILL_SCRIPY = new DefaultRedisScript();
        SEC_KILL_SCRIPY.setLocation(new ClassPathResource("seckill.lua"));
        SEC_KILL_SCRIPY.setResultType(Long.class);
    }


    @PostConstruct
    private void init() {

        order_task_executor.submit(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        //对于阻塞队列，如果队列为null，则会阻塞，直到队列中有成员进入。所以不必担心while(true)一直循环导致cpu消耗过大
                        VoucherOrder order = ORDER_QUEUE.take();
                        /**
                         * 注解@Transactional要生效，需要获得代理类。而AopContext.currentProxy()是与线程相关的。
                         * 其存储到ThreadLocal中。所以我们只能在主线程中去获取proxy。而这里想要在新开启的线程中使用的
                         * 话，只能是在主线程中获取到然后作为类的一个变量保存起来，在这里使用
                         */
                        proxy.createOrder(order);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        log.info("开启独立线程，负责从阻塞队列中取出订单入库.....");

    }


    @Override
    public  Result seckill(Long voucherId) {
        /**
         * 思路: 在redis中完成 库存是否充足的判断、 用户是否重复下单
         */

        Long userId = UserHolder.getUser().getId();
        Long result = stringRedisTemplate.execute(SEC_KILL_SCRIPY, Collections.emptyList(),voucherId.toString(), userId.toString());
        if (result != 0) {
            //善用三位运算符，来减少if嵌套
            return Result.fail(result == 1 ? "优惠券已售光":"不能重复下单");
        }
        Long orderId = redisIdWorker.nextId(SEC_KILL);
        proxy = (VoucherOrderServiceImpl4) AopContext.currentProxy();

        try {
            VoucherOrder voucherOrder = new VoucherOrder();
            voucherOrder.setUserId(userId);
            voucherOrder.setVoucherId(voucherId);
            voucherOrder.setId(orderId);
            ORDER_QUEUE.put(voucherOrder);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.ok(orderId);
    }




    @Transactional
    public void createOrder(VoucherOrder voucherOrder) {
        //一人一单，先查询数据库中该用户是否已经下单
        QueryWrapper<VoucherOrder> condition = new QueryWrapper<VoucherOrder>().eq("voucher_id", voucherOrder.getVoucherId()).eq("user_id", voucherOrder.getUserId());
        int count = count(condition);
        if (count > 0) {
            log.error("一人只能下一单");
            return;
        }

        //2.扣减订单库存
        boolean success = seckillVoucherService.update().setSql("stock = stock - 1").gt("stock", 0).eq("voucher_id", voucherOrder.getVoucherId()).update();
        if (!success) {
            log.error("优惠券已销售完");
        }

        //3.生成订单
        voucherOrderMapper.insert(voucherOrder);
        log.info("入库{}的订单成功", voucherOrder.getId());
    }


    @Override
    public Result createOrder(Long voucherId, SeckillVoucher seckillVoucher) {
        log.info("该方法在这个类中没用.....");
        return null;
    }
}
